home *** CD-ROM | disk | FTP | other *** search
- /************************************************************************/
- /* */
- /* Hoser BackGammon version 1.0 */
- /* */
- /* Robert Pfister */
- /* */
- /* Rfd#3 Box 2340 home:(207)-873-3520 */
- /* Waterville, Maine 04901 */
- /* */
- /* Pfister_rob%dneast@dec.decwrl */
- /* */
- /* */
- /* Copyright June,1987 */
- /* */
- /* This program will play a game of backgammon at the novice level */
- /* */
- /* The code is in 4 parts... */
- /* / */
- /* \/ 1) back.c - main driver */
- /* 2) eval.c - evaluation of moves */
- /* 3) backscn.c - screen stuff.. */
- /* 4) backmenu.c - menu stuff, help text, and "decoder" */
- /* */
- /* this was compiled under Manx 3.20a, using long integers */
- /* */
- /************************************************************************/
- /* Contains: */
- /* ShowStandings main Restart StoreMove */
- /* RecallMove Won UserMove Roll */
- /* GameOver stats NewGame valid */
- /* update */
- /* most recent modification 11/ 8/93 E.M.Greene */
- /* most recent modification 2/14/94 E.M.Greene */
- /* most recent modification 3/ 9/95 E.M.Greene */
- /* most recent modification 7/ 1/95 E.M.Greene */
-
- /* Revisions, corrections, and improvements include:
- 1. Corrected errors in provided source code:
- a. InitArea() call in Gsetup had incorrect value for number of points
- thereby resulting in no pieces being displayed.
- b. TextScreen() failed to perform CloseDevice() upon completion
- thereby resulting in strange phenomena after being called
- c. stats() failed to display anything if program initiated from
- Workbench. Revised to call TextScreen.
- d. Bug in eval() resulted in poor performance. Correction made the
- computer a much more vigorous opponent -- it'll now eat your lunch
- a goodly percentage of the time.
- e. Numerous grammatical and spelling errors corrected, mainly "peice".
- f. Revised the process of displaying numbers on pieces when more than
- PerSpike pieces are on one point so that counts greater than nine
- are shown correctly.
- g. Corrected error in StoreMove so that "stack" overflow is properly
- detected.
-
- h. Added code to reset StackPos at start of new game. Otherwise, if
- you play long enough, the stack would overflow and the bug of
- preceding item will result in a flameout.
- i. Eliminated Gerror from BackScreen and replaced with Alerts. Gerror
- was trying to display error message about screen or window opening
- failures on a screen or window which hadn't opened!
-
- 2. Revisions:
- a. Eliminated dead code and variables. Variable names were revised in
- a number of places to be more meaningful. Reformatted source and
- added comments to make the source more readable.
- b. Merged IntuiMessage processing into one routine and revised to look
- for all messages currently queued. Menu operations are now much
- more effective.
- c. Revised the dice "throw" generation to reduce the instances of the
- same values being repeatedly generated.
- d. Reduced declared sizes for AreaBuf and StackSize to something more
- reasonable.
- e. Replaced the calls to the lightly-documented function RangeRand
- with calls to Scott McMahan's implementation of the algorithm of
- Pierre L'Ecuyer [L'Ecuyer, P. Efficient and Portable Combined Random
- Number Generators. Commun. ACM 31, 6 (June, 1988) 742].
- f. Replaced the routine CopyBoard in BackEval with a macro invoking
- memcpy().
- 3. Enhancements:
- a. Array point[] was added to eliminate need to constantly compute
- board positions which are constants.
- b. Loop count in BlinkPiece() was reduced to expedite moves.
- c. Only one pair of dice are displayed thereby eliminating confusion
- about which pair of numbers human is to play.
- d. Revised color specification such that ColorRequestor can potentially
- be added.
- e. Added readout of relative standings of players.
- f. Recompiled with Lattice C resulting in more randomness in dice throws.
- [SAS Institute had ought to know how to produce a random number generator
- since they are in the statistical analysis business!]
- g. Revised game statistics to include number of points rolled but not
- used ("wasted"). Also, now show average roll to one decimal place.
- h. Revised as required to compile cleanly with SAS/C 6.3
- i. Added "cost" of coming off bar to "wasted" count (11/16/94)
- j. Added counting of total rolls and display (2/14/94).
- k. Added total games and effective average roll to data display (12/27/95)
- */
- #include <proto/exec.h>
- #include <proto/graphics.h>
- #include <proto/intuition.h>
- #include <libraries/dos.h>
- #include "back.h"
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <stddef.h>
- #include <math.h>
-
- #define StackSize 100
-
- extern int AvgPos;
- extern int Moves;
- extern int minY;
- extern struct RastPort *rp;
- extern struct Screen *screen;
-
- you_me_rec you, me;
- USHORT total_games = 0;
- static signed char
- BoardPos[StackSize][26],/* stack of the previous positions */
- DicePos[StackSize][4], /* stack of the dice rolls */
- StackPos, /* how many moves back are we at? */
- MoveList[4], /* stack of this turn's moves */
- CurMove = 0; /* place in the list */
- signed char Turn, /* whose turn is it? */
- board[26], /* describes the board position */
- /* 0 and 25 are the bar */
- Dice[4]; /* describes the 2 or 4 dice availible */
- const char version[] = "$VER: Backgammon 2.2 (95/12/27)";
-
- void ShowStandings(void)
- {
- char s[6];
- int Msum=0, Ysum=0, i;
-
- for(i = 0; i <= 24; i++)
- if (board[i] > 0)
- Msum += (25 - i) * board[i];
- for(i = 1; i <= 25; i++)
- if (board[i] < 0)
- Ysum -= i * board[i];
- /* write box scores at top of window */
- SetAPen(rp, tm1_color);
- RectFill(rp, 400L, minY-3-screen->Font->ta_YSize, 520, minY-2);
- SetAPen(rp,dice_color);
- sprintf(s,"%4d", Msum);
- if (board[0] != 0)
- strcat(s,"+");
- Move(rp, 400L, (long)minY - 6);
- Text(rp,s,strlen(s));
- sprintf(s,"%4d", Ysum);
- if (board[25] != 0)
- strcat(s,"+");
- Move(rp, 460L, (long)minY - 6);
- Text(rp, s, strlen(s));
- }
-
- double random(void);
-
- unsigned short real_rand(void)
- {
- return (unsigned short)(random() * 6.0);
- }
-
- int Roll(BYTE d[4]) /* roll the dice (4 results) */
- {
- int n;
-
- d[2] = d[3] = 0;
- for (n = (real_rand() >> 5) & 7; n >= 0; n--)
- d[0] += real_rand();
- d[1] = (abs(d[0]) % 6) + 1; /* RangeRand(6L)+1; */
- for (n = (real_rand() >> 7) & 7; n >= 0; n--)
- d[0] += real_rand();
- d[0] = (abs(d[0]) % 6) + 1; /* RangeRand(6L)+1; */
- if (d[0]==d[1]) { /* if rolled doubles */
- d[2] = d[3] = d[1];
- return 1;
- }
- else
- return 0;
- }
-
- int NewGame(signed char board[26], signed char Dice[4])
- {
- int i;
-
- StackPos = 0;
- memset(board, 0, sizeof_board); /* initialize the board */
-
- board[ 1]= 2; board[24]=-2;
- board[12]= 5; board[ 6]=-5;
- board[17]= 3; board[ 8]=-3;
- board[19]= 5; board[13]=-5;
- memset(&you, 0, offsetof(you_me_rec, total.roll));
- memset(&me, 0, offsetof(you_me_rec, total.roll));
- AvgPos = 0;
-
- for (i = 0; i <= 25;i ++)
- PutSpike(i, board[i]);
-
- while (Roll(Dice)); /* until no doubles */
- if (Dice[0]>Dice[1]) /* see who goes first */
- return(Uside);
- else
- return(Cside);
- }
-
- #include "intuition/intuition.h"
- extern struct Window *w;
-
- int UserMove(BYTE board[26], BYTE Dice[4], int side)
- {
- BYTE Pick = 0, Valid = 0; /* flags */
- int i,x,y,
- from_point, to_point;
- struct IntuiMessage *message;
- unsigned long Class, msg_flag = 1L << w->UserPort->mp_SigBit;
- unsigned short Code;
-
- while (Valid == 0) {
- Wait(msg_flag);
- while (message=(struct IntuiMessage *) GetMsg(w->UserPort)) {
- Code = message->Code;
- Class = message->Class;
- x = message->MouseX;
- y = message->MouseY;
- ReplyMsg((struct Message *)message);
- if (Class == MENUPICK) {
- if (Code != MENUNULL)
- Valid=DoMenu((MENUNUM(Code)*100)+(ITEMNUM(Code)*10)+SUBNUM(Code));
- }
- /* check if mouse pressed..... */
- else if ((Class == MOUSEBUTTONS) && (Code == SELECTDOWN)) {
- if ((to_point = decode(x,y)) == -1) { /* clicked dice */
- you.wasted += Dice[0] + Dice[1] + Dice[2] + Dice[3];
- Dice[0] = Dice[1] = Dice[2] = Dice[3] = 0;
- Valid = 1;
- you.incomplete++;
- }
- else
- if (Pick == 0) { /* first click */
- if (board[to_point] * side <= 0)
- DoMenuStrip("No piece there!!");
- else {
- Pick = 1;
- from_point = to_point;
- }
- break;
- }
-
- else if (Pick == 1) { /* second click */
- Pick = 0;
- for (i = 0; (Valid == 0) && (i < 4); i++)
- if ((Dice[i] != 0) &&
- (valid(board, from_point, to_point, Dice[i]))) {
- if (from_point == 25)
- you.wasted += 25 - to_point;
- if (from_point == to_point)
- you.wasted += (Dice[i] - from_point);
- BlinkPiece(board, from_point);
- update(board, from_point, to_point, side);
- PutSpike(from_point , board[from_point]);
- BlinkPiece(board, to_point);
- PutSpike(0 , board[ 0]);
- PutSpike(25, board[25]);
- Valid = 1;
- Dice[i] = 0;
- }
- UnDoMenuStrip();
- } /* end Pick == 1 */
- /* end else decode */
- if (Valid == 0)
- DoMenuStrip("Not Valid move");
- }; /* end if mousebutton */
- } /* end while message */
- } /* end while Valid */
- return (Valid != -1);
- } /* end UserMove */
-
- void StoreMove(void)
- {
- if (StackPos < StackSize - 1) { /* can only store that many */
- memcpy(&BoardPos[StackPos][0], board, sizeof board);
- memcpy(&DicePos[StackPos][0], Dice, sizeof Dice);
- StackPos++;
- }
- /* printf("STORE: stack is at %d \n",StackPos); */
- }
-
- void stats(void)
- {
- char *stat_data[] = {
- " Game Statistics",
- "\n",
- " Number of games: xx\n ",
- "\n",
- " Number of moves xx; Avg positions evaluated xxxx \n",
- "\n",
- " Avg. roll : User xx.x Computer xx.x \n ",
- " Effect. avg.: User xx.x Computer xx.x \n ",
- " Doubles : User xx Computer xx \n ",
- " Inc. moves : User xx Computer xx \n ",
- " Turns on bar: User xx Computer xx \n ",
- " Wasted : User xx Computer xx \n ",
- "\n",
- " [click in window to continue]\n "};
-
- if ((you.moves != 0) && (me.moves != 0)) {
- sprintf(stat_data[2], "Number of games: %d\n", ++total_games);
- sprintf(stat_data[4]," Number of moves %d;"
- " Avg positions evaluated %d\n", me.moves, AvgPos / me.moves);
- sprintf(stat_data[6]," Avg roll : User %5.1f Computer %5.1f \n",
- (float)you.dice_total / you.moves, (float)me.dice_total / me.moves);
- sprintf(stat_data[7]," Effect. avg.: User %5.1f Computer %5.1f \n",
- (float)(you.dice_total - you.wasted) / you.moves,
- (float)(me.dice_total - me.wasted) / me.moves);
- sprintf(stat_data[8]," Doubles : User %2d Computer %2d \n",
- you.doubles, me.doubles);
- sprintf(stat_data[9]," Inc. moves : User %2d Computer %2d \n",
- you.incomplete, me.incomplete);
- sprintf(stat_data[10]," Turns on bar: User %2d Computer %2d\n",
- you.on_bar, me.on_bar);
- sprintf(stat_data[11]," Wasted : User %3d Computer %3d\n",
- you.wasted, me.wasted);
- TextScreen(stat_data, 14);
- you.total.roll += you.dice_total;
- me.total.roll += me.dice_total;
- you.total.average_roll += (float)you.dice_total / you.moves;
- me.total.average_roll += (float)me.dice_total / me.moves;
- you.total.incomplete += you.incomplete;
- me.total.incomplete += me.incomplete;
- you.total.wasted += you.wasted;
- me.total.wasted += me.wasted;
- }
- }
-
- void GameOver(BOOL show_stats)
- {
- if (show_stats)
- stats();
- finit();
- exit(0);
- }
-
- int Won(BYTE board[26], int side)
- {
- int i;
-
- if (side == 1) {
- for(i = 0;i <= 25;i++)
- if (board[i] > 0)
- return 0;
- }
- else
- for(i = 0;i <= 25; i++) {
- if (board[i] < 0)
- return 0;
- }
- return(1);
- }
-
- void setseed(void);
-
- main() /* main procedure */
- {
- Gsetup();
- memset(&you, 0, sizeof you);
- memset(&me, 0, sizeof me);
- setseed();
- Turn = NewGame(board,Dice);
- for (;;) { /* go until game is over */
- if (Turn == Uside)
- DoMenuStrip("Your turn");
- else
- DoMenuStrip("My turn");
- ShowDice(Dice,Turn);
- ShowStandings();
- if (Turn == Uside) {
- StoreMove(); /* store this board position */
- /* user stats */
- if (Dice[2] > 0)
- you.doubles++;
- you.dice_total += Dice[0] + Dice[1] + Dice[2] + Dice[3];
- you.moves++;
- if (board[25] < 0)
- you.on_bar++;
- /* do the Move */
- do
- if (UserMove(board,Dice,Turn) == 0)
- GameOver(TRUE); /* escape */
- while ((Turn == Uside) && (Won(board,Turn) == 0) &&
- (Dice[0] + Dice[1] + Dice[2] + Dice[3]!=0));
- you.wasted += (Dice[0] + Dice[1] + Dice[2] + Dice[3]);
- }
-
- else { /* turn == Cside */
- /* computer stats */
- if (Dice[2] > 0)
- me.doubles++;
- me.dice_total += Dice[0] + Dice[1] + Dice[2] + Dice[3];
- me.moves++;
- if (board[0] > 0)
- me.on_bar++;
- GenerateMoves(board,Dice); /* do move */
- DoMove(board,Dice);
- }
- Turn = -Turn;
- Roll(Dice);
- if (Turn == 0) /* signal to restart the game */
- Turn = NewGame(board,Dice);
- if (Won(board,Uside)) { /* did user win yet? */
- ShowStandings();
- stats();
- if (requestor("I lose. Play again?","Yes","No"))
- GameOver(FALSE);
- else
- Turn = NewGame(board,Dice);
- }
- if (Won(board,Cside)) { /* did computer win yet? */
- ShowStandings();
- stats();
- if (requestor("Hee hee. I Win. Play Again?","Yes","No"))
- GameOver(FALSE);
- else
- Turn = NewGame(board,Dice);
- }
- } /* end do forever */
- } /* end of main */
-
- void Restart(void)
- {
- Turn = 0;
- }
-
- void RecallMove(int how)
- {
- int i;
-
- StackPos = StackPos-1 + how;
- if (StackPos < 0)
- StackPos = 0;
- for (i = 0; i < 26; i++)
- if (board[i] != BoardPos[StackPos][i]) { /* only refresh stuff changed */
- board[i] = BoardPos[StackPos][i];
- PutSpike(i,board[i]);
- }
- for (i = 0; i < 4; i++)
- Dice[i] = DicePos[StackPos][i];
- ShowDice(Dice,Uside);
- StackPos++;
- /* printf("RECALL: stack is at %d \n",StackPos); */
- }
-
- int valid(BYTE board[26], int from_point, int to_point, int die_value)
- { /* is the move valid for the board and one die? */
- BYTE sign, i, All_In,
- *bp;
-
- All_In = -1;
- if (Turn == Cside) { /* computer's move */
- if ((board[0] != 0) && (from_point != 0))
- return(0); /* man on bar and didn't select bar */
- sign = Cside;
- /* see if all pieces are in home base */
- bp = &board[1];
- for(i = 1; i <= 24; i++)
- if (*(bp++) > 0) { /* find man fartherest from home */
- All_In = i;
- break;
- }
- /* check if can take it off */
- if (to_point > 24) { /* if in home board */
- if ((All_In > 18) && /* if all men in home board */
- ((from_point == (25 - die_value)) ||
- ((25 - die_value) < All_In) && (from_point == All_In)))
- return(1);
- else
- return(0);
- }
- }
-
- else { /* human's move */
- if ((board[25] != 0) && (from_point != 25))
- return(0); /* man on bar and didn't select bar */
- sign = Uside;
- /* see if all pieces are in home base */
- bp = &board[25];
- for(i = 24; i >= 1; i--)
- if (*(--bp) < 0) { /* find man fartherest from home */
- All_In = i;
- break;
- }
- /* see if can take off */
- if (to_point == from_point) { /* double-clicked? */
- if ((All_In < 7) && /* all pieces in home board */
- ((from_point == die_value) ||
- ((die_value > All_In) && (from_point == All_In))))
- return(1);
- else
- return(0);
- }
- }
- if ((to_point-from_point) * sign != die_value)
- return(0); /* distance to move != die value */
- if (board[to_point] * sign < -1)
- return(0); /* can't land at "to" point */
- return(1); /* I suppose that's all there is.. */
- }
-
- int update(BYTE *board, int from_point, int to_point, BYTE sign)
- { /* put a given move onto the board */
- /* remove the piece from the source spike */
- board[from_point] -= sign;
- /* if taking a piece off board or bar, don't go any further */
- if ((to_point == from_point) || (to_point >= 25))
- return(1);
- /* add a piece to the destination spike and determine if capturing */
- board[to_point] += sign;
- if (board[to_point] == 0) { /* must have hit opponent */
- int bar = 0;
-
- if (sign < 0)
- bar = 25;
- board[to_point] = sign; /* put us onto point */
- board[25-bar] -= sign; /* put opponent on bar */
- }
- return(0);
- }
-